//
//  CJCRCHub.swift
//  CJPack
//
//  Created by 陈警卫 on 2025/7/11.
//

import Foundation

public class CJCRCHub: NSObject {

    public static func computeCRC8(
        _ data: [UInt8],
        initCrc: UInt8 = 0x00,
        polynom: UInt8 = 0x07,
        xor: UInt8 = 0x00,
        refIn: Bool = false,
        refOut: Bool = false
    ) -> UInt8 {
        
        var crc = initCrc
        for byte in data {
            let input = refIn ? CJCRCTool.reverseBits(byte) : byte
            crc ^= input
            
            for _ in 0..<8 {
                if (crc & 0x80) != 0 {
                    crc = (crc << 1) ^ polynom
                } else {
                    crc <<= 1
                }
            }
        }
        
        if refOut {
            crc = CJCRCTool.reverseBits(crc)
        }
        return crc ^ xor
    }

    public static func computeCRC16(
        _ data: [UInt8],
        initCrc: UInt16 = 0xFFFF,
        polynom: UInt16 = 0x1021,
        xor: UInt16 = 0x0000,
        refIn: Bool = false,
        refOut: Bool = false
    ) -> UInt16 {
        
        var crc = initCrc
        for byte in data {
            let input = refIn ? CJCRCTool.reverseBits(byte) : byte
            crc ^= UInt16(input) << 8

            for _ in 0..<8 {
                if (crc & 0x8000) != 0 {
                    crc = (crc << 1) ^ polynom
                } else {
                    crc <<= 1
                }
            }
        }

        if refOut {
            crc = CJCRCTool.reverseBits(crc)
        }
        return crc ^ xor
    }

    
    public static func computeCRC32(
        _ data: [UInt8],
        initCrc: UInt32 = 0xFFFFFFFF,
        polynom: UInt32 = 0x04C11DB7,
        xor: UInt32 = 0xFFFFFFFF,
        refIn: Bool = true,
        refOut: Bool = true
    ) -> UInt32 {
        
        var crc = initCrc
        for byte in data {
            let input = refIn ? CJCRCTool.reverseBits(byte) : byte
            crc ^= UInt32(input) << 24

            for _ in 0..<8 {
                if (crc & 0x80000000) != 0 {
                    crc = (crc << 1) ^ polynom
                } else {
                    crc <<= 1
                }
            }
        }

        if refOut {
            crc = CJCRCTool.reverseBits(crc)
        }
        return crc ^ xor
    }
    
}

public extension CJCRCHub {
    
    static func computeCRC8(_ data: [UInt8], type: CJCRC8Type = .autosar) -> UInt8 {
        let config = CJCRCConfigTool.config(type)
        return computeCRC8(
            data,
            initCrc: config.initCrc,
            polynom: config.polynom,
            xor: config.xor,
            refIn: config.refIn,
            refOut: config.refOut
        )
    }
    
    static func computeCRC16(_ data: [UInt8], type: CJCRC16Type = .arc) -> UInt16 {
        let config = CJCRCConfigTool.config(type)
        return computeCRC16(
            data,
            initCrc: config.initCrc,
            polynom: config.polynom,
            xor: config.xor,
            refIn: config.refIn,
            refOut: config.refOut
        )
    }
    
    static func computeCRC32(_ data: [UInt8], type: CJCRC32Type = .aixm) -> UInt32 {
        let config = CJCRCConfigTool.config(type)
        return computeCRC32(
            data,
            initCrc: config.initCrc,
            polynom: config.polynom,
            xor: config.xor,
            refIn: config.refIn,
            refOut: config.refOut
        )
    }
}

extension CJCRCHub {
    
    @objc public static func computeCRC8(data: NSData, type: CJCRC8Type) -> UInt8 {
        let byteArray = [UInt8](data as Data)
        return computeCRC8(byteArray, type: type)
    }
    
    @objc public static func computeCRC16(data: NSData, type: CJCRC16Type) -> UInt16 {
        let byteArray = [UInt8](data as Data)
        return computeCRC16(byteArray, type: type)
    }
    
    @objc public static func computeCRC32(data: NSData, type: CJCRC32Type) -> UInt32 {
        let byteArray = [UInt8](data as Data)
        return computeCRC32(byteArray, type: type)
        
    }
}
